package com.lam.common.utils;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lam.common.exception.BaseException;

/**
 * 时间工具类
 * 
 * @author lam
 */
public class DateUtil extends DateUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);
	
	public static final String PATTERN_YYYY = "yyyy";
	public static final String PATTERN_YYYY_MM = "yyyy-MM";
    public static final String PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss";
    public static final String PATTERN_YYYY_MM_DD = "yyyy-MM-dd";
    public static final String PATTERN_YYYYMMDD = "yyyyMMdd";
    public static final String PATTERN_YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
    public static final String PATTERN_YYYYMMDDHHMMSS_SSS = "yyyyMMddHHmmssSSS";
    public static final String PATTERN_HHMMSS = "HH:mm:ss";

    public static final String PATTERN_YYYYMMDDHHMMSS_1 = "yyyy/MM/dd HH:mm:ss";
    public static final String PATTERN_YYYY_MM_DD_1 = "yyyy/MM/dd";
    
    /**1分钟时长（60秒）*/
    public static final long ONE_SECOND_LONG = 60;
    /**1分钟时长（60000毫秒）*/
    public static final long ONE_SECOND_M_LONG = ONE_SECOND_LONG*1000;
    /**5秒钟时长（5000毫秒）*/
    public static final long FIVE_SECOND_M_LONG = 5000;
    /** 1小时的时长（秒） */
    private static final long ONE_HOUR_S_LONG = 60 * ONE_SECOND_LONG;
    /** 1小时的时长（毫秒） */
    private static final long ONE_HOUR_M_LONG = 60 * ONE_SECOND_M_LONG;
    /** 一天的秒数 */
    public static final long ONE_DAY_S_LONG = ONE_HOUR_S_LONG*24;
    /** 一天的毫秒数 */
    public static final long ONE_DAY_M_LONG = ONE_HOUR_M_LONG*24;
    
    
    
    private static final String[] PARSE_PATTERNS = { PATTERN_YYYY_MM_DD, PATTERN_DEFAULT, "yyyy-MM-dd HH:mm", PATTERN_YYYY_MM,
			"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss",
			"yyyy.MM.dd HH:mm", "yyyy.MM" };

    private DateUtil(){
		throw new IllegalStateException("Utility class");
	}
    
	/**
	 * 获取服务器启动时间
	 */
	public static Date getServerStartDate() {
		long time = ManagementFactory.getRuntimeMXBean().getStartTime();
		return new Date(time);
	}

	/**
	 * 计算两个时间差
	 */
	public static String getDatePoor(Date endDate, Date nowDate) {
		long nd = 1000 * 24 * 60 * 60;
		long nh = 1000 * 60 * 60;
		long nm = 1000 * 60;
		// 获得两个时间的毫秒时间差异
		long diff = endDate.getTime() - nowDate.getTime();
		// 计算差多少天
		long day = diff / nd;
		// 计算差多少小时
		long hour = diff % nd / nh;
		// 计算差多少分钟
		long min = diff % nd % nh / nm;
		// 计算差多少秒//输出结果
		return day + "天" + hour + "小时" + min + "分钟";
	}

    /**
     * 获取指定天后的时间 
     * @param n n天后的时间, 若n小于0，则表示|n|天前的时间
     * @return
     */
    public static Date getDurationDate(int n){
    	return new Date(System.currentTimeMillis() + ONE_DAY_M_LONG*n);
    }
    
    /**
     * 获取指定天后的时间 
     * @param date 时间
     * @param n n天后的时间, 若n小于0，则表示|n|天前的时间
     * @return
     */
    public static Date getDurationDate(Date date, int n){
    	return new Date(date.getTime() + ONE_DAY_M_LONG*n);
    }

	/**
	 * 获取指定分钟后的时间
	 * @param date 时间
	 * @param n n分钟后的时间, 若n小于0，则表示|n|分钟前的时间
	 * @return
	 */
    public static Date getDurationMinute(Date date, int n){
    	return new Date(date.getTime() + ONE_SECOND_M_LONG*n );
    }

    /**
     * 获取指定天后的时间 
     * @param n n天后的时间, 若n小于0，则表示|n|天前的时间
     * @return
     */
    public static String getDurationDateStr(int n){
    	return getDurationDateStr(n, PATTERN_YYYY_MM_DD);
    }
    
    /**
     * 获取指定天后的时间 
     * @param n n天后的时间, 若n小于0，则表示|n|天前的时间
     * @return
     */
    public static String getDurationDateStr(int n, String pattern){
    	return formatDate(getDurationDate(n), pattern);
    }
    
    /**
	 * 获取当前Date型日期
	 * 
	 * @return Date() 当前日期
	 */
	public static Date newDate() {
		return new Date();
	}

	/**
	 * 获取当前日期, 默认格式为yyyy-MM-dd
	 * 
	 * @return String
	 */
	public static String getCurDate() {
		return getDate(PATTERN_YYYY_MM_DD);
	}
	
	public static final String getCurTime() {
		return getDate(PATTERN_DEFAULT);
	}
	
	public static final String getDate(String pattern) {
		return formatDate(newDate(), pattern);
	}

    public static String formatDateTime(Date date){
        return formatDate(date, PATTERN_DEFAULT);
    }
    
    public static String formatDate(Date date){
        return formatDate(date, PATTERN_YYYY_MM_DD);
    }
    
    public static String formatDate(Date date, String pattern){
        return new SimpleDateFormat(pattern).format(date);
    }

    public static Date parseDate(String str) {
    	if(StringUtils.isBlank(str)) {
    		return null;
    	}
        try {
			return parseDate(str, PARSE_PATTERNS);
		} catch (ParseException e) {
			logger.error(e.getMessage(), e);
			return null;
		}
    }
    
    public static Date parseSampleDate(String str) {
    	if(StringUtils.isBlank(str)) {
    		return null;
    	}
        try {
			return parseDate(str, PATTERN_YYYY_MM_DD, PATTERN_YYYYMMDD);
		} catch (ParseException e) {
			logger.error(e.getMessage(), e);
			return null;
		}
    }
    
    /**
     * 比较两个时间的相差值, 返回天数, dateStr2 - dateStr1
     * @param dateStr1
     * @param dateStr2
     * @return
     */
    public static int getDiffCount(String dateStr1, String dateStr2){
    	Date date1 = parseSampleDate(dateStr1);
    	Date date2 = parseSampleDate(dateStr2);
    	if(date1 == null || date2 == null){
    		logger.error("两个时间有至少一个是空值，无法比较! date1:{}, date2:{}", dateStr1, dateStr2);
    		throw new BaseException("两个时间有至少一个是空值，无法比较!");
    	}
    	return getDiffCount(date1, date2);
    }
    
    /**
     * 比较两个时间的相差值, 返回天数, dateStr2 - dateStr1
     * @param dateStr1
     * @param dateStr2
     * @return
     */
    public static int getDiffCount(Date date1, Date date2){
    	if(date1 == null || date2 == null){
    		logger.error("两个时间有至少一个是空值，无法比较! date1:{}, date2:{}", date1, date2);
    		throw new BaseException("两个时间有至少一个是空值，无法比较!");
    	}
    	long du = (date2.getTime() - date1.getTime())/ONE_DAY_M_LONG;
    	if(du < 0){
    		return -1;
    	}
    	return (int) (du+1);
    }
    
    /**
     * 获取两个日期中间的日期集合
     * @param dateStr1
     * @param dateStr2
     * @return
     */
    public static List<Date> getDateDuration(Date date1, Date date2){
    	if(date1 == null || date2 == null){
    		logger.error("两个时间有至少一个是空值，无法比较! date1:{}, date2:{}", date1, date2);
    		throw new BaseException("两个时间有至少一个是空值，无法比较!");
    	}
    	long du = (date2.getTime() - date1.getTime())/ONE_DAY_M_LONG;
    	if(du < 0){
    		return Collections.emptyList();
    	}
    	List<Date> dateList = new ArrayList<>();
    	for (int i = 0; i <= du; i++) {
			dateList.add(new Date(date1.getTime() + ONE_DAY_M_LONG*i));
		}
    	return dateList;
    }
    
    /**
     * 获取两个日期中间的日期集合
     * @param dateStr1
     * @param dateStr2
     * @return
     */
    public static List<String> getDateDuration(Date date1, Date date2, String pattern){
    	List<Date> dateList = getDateDuration(date1, date2);
    	if(CollectionUtil.isEmpty(dateList)){
    		return Collections.emptyList();
    	}
    	
    	List<String> strList = new ArrayList<>();
    	for (Date date : dateList) {
    		strList.add(formatDate(date, pattern));
		}
    	return strList;
    }
    
    public static boolean isLeepYear(int year){
    	return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
    }
    
    public static int getMonthDayCount(int year, int month){
    	switch (month) {
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:return 31;
		case 4:
		case 6:
		case 9:
		case 11:return 30;
		case 2:return isLeepYear(year)?29:28;
		default:
			return -1;
		}
    }
    
    public static String getDurationMonthStr(int n){
    	return formatDate(getDurationMonth(new Date(), n), PATTERN_YYYY_MM_DD);
    }
    
    public static String getDurationMonthStr(int n, String pattern){
    	return formatDate(getDurationMonth(new Date(), n), pattern);
    }
    
    public static Date getDurationMonth(int n){
    	return getDurationMonth(new Date(), n);
    }
    
    /**
     * 获取指定月数后的时间 
     * @param date
     * @param n 月后的时间, 若n小于0，则表示|n|月前的时间
     * @return
     */
    public static Date getDurationMonth(Date date, int n){
    	if(n == 0){
    		return date;
    	}
    	Calendar c = Calendar.getInstance();
    	c.setTime(date);
    	int year = c.get(Calendar.YEAR);
    	int m = c.get(Calendar.MONTH)+1;
    	int d = c.get(Calendar.DAY_OF_MONTH);
    	int totalDayCount = 0;
    	if(n > 0){
    		do{
    			totalDayCount += getMonthDayCount(year, m);
    			m++;
    			n--;
    			if(m>12){
    				year++;
    				m=m-12;
    			}
        	}while(n>0);
    		int newMonthDayCount = getMonthDayCount(year, m);
    		if(d > newMonthDayCount){
    			totalDayCount -= d - newMonthDayCount;
    		}
    		return getDurationDate(date, totalDayCount);
    	}
    	
		int newMonthDayCount; 
		do{
			m--;
			if(m<1){
				year--;
				m=12;
			}
			newMonthDayCount = getMonthDayCount(year, m);
			totalDayCount += newMonthDayCount;
			n++;
    	}while(n<0);
		if(d > newMonthDayCount){
			totalDayCount += d - newMonthDayCount;
		}
		totalDayCount = -totalDayCount;
		return getDurationDate(date, totalDayCount);
    }
    
    public static String costTime(long startTime) {
        return convertTimeLong(System.currentTimeMillis() - startTime);
    }

    /**
     * 将毫秒转为时分秒
     *
     * @param timeLong
     * @return
     */
    public static String convertTimeLong(long timeLong) {
        StringBuilder sb = new StringBuilder();
        if (timeLong > ONE_DAY_M_LONG) {
            sb.append(timeLong / ONE_DAY_M_LONG).append("天");
            timeLong = timeLong % ONE_DAY_M_LONG;
        }
        if (timeLong > ONE_HOUR_M_LONG) {
            sb.append(timeLong / ONE_HOUR_M_LONG).append("小时");
            timeLong = timeLong % ONE_HOUR_M_LONG;
        }
        if (timeLong > ONE_SECOND_M_LONG) {
            sb.append(timeLong / ONE_SECOND_M_LONG).append("分钟");
            timeLong = timeLong % ONE_SECOND_M_LONG;
        }
        if (timeLong > 1000) {
            sb.append(timeLong / 1000).append("秒");
            timeLong = timeLong % 1000;
        }
        if (timeLong > 0) {
            sb.append(timeLong).append("毫秒");
        }
        return sb.length() > 0 ? sb.toString() : "0毫秒";
    }
    
    
    /**
     * 转换时间为时间段对象
     * @param ttlStr
     * <br>例：
     * <pre>
     *  1天：1d或1D
     *  2小时：2h或2H 
     *  10分钟：10m或10M
     *  30秒：30s或30S
     * </pre>
     * @return
     */
    public static Duration parseTtl(String ttlStr){
    	if(StringUtils.isBlank(ttlStr)) {
    		return null;
    	}
    	ttlStr = ttlStr.trim();
    	long ttlLong = NumberUtil.toLong(ttlStr.substring(0, ttlStr.length()-1));
        if (ttlLong < 1) {
        	return null;
        }
    	char unit = ttlStr.charAt(ttlStr.length()-1);
    	switch (unit) {
    	//yyyy/MM/dd HH:mm:ss
    	//天
		case 'D': 
		case 'd': return Duration.ofDays(ttlLong);
		//小时
		case 'H': 
		case 'h': return Duration.ofHours(ttlLong);
		//分钟
		case 'M': 
		case 'm': return Duration.ofMinutes(ttlLong);
		//秒
		case 'S': 
		case 's': return Duration.ofSeconds(ttlLong);
		default://其他不支持 
			return null;
		}
    }
    
}

